#include <iostream>

#include <cmath>

#include <string>

#include <list>

#include "string.h"

using namespace std;

// //什么是左值?为什么是左值引用?
// /*
//     左值在最初设定时即为处于赋值符号的左侧 故称为左值
//     而右值被放置为赋值符号的右侧故被称为右值
   
//     而再C++11版本更新(引入)了revalue reference的概念即右值引用
//     在引入右值引用过后左值与右值之间的关系不再和赋值符号有关
//       -左值: 在C++11后 左值的定义不再仅仅赋值符号有关 左值实际上是指具有标识符的对象 且可以取地址
//         并且可以进行引用 通常左值具有确定的内存位置

//       -右值: 在C++11中,右值的定义拓展为不具有标识符的临时对象 或者可以被移动但不能被赋值的对象,右值
//         通常是临时创建的且没有命名 故不能通过取地址来获取其地址

// */

// int main() {
//   // cout << "hello world" << endl;

//   int a1 = 1, b1 = 2;
//   a1 = b1;//在C++11之前该种情况下a1为左值b1为右值 
//   //但在C++11后的概念当中 a1与 b1都为左值

//   //--------

//   // 以下的p b c *p皆为左值
//   int *p = new int(0);
//   int b = 1;
//   const int c = 2;

//   //对上面的左值进行引用
//   int*& rp = p;
//   int& rb = b;
//   const int& rc = c;
//   int& pvalue = *p;

//   return 0;
// }


// ########################################

// // 什么是右值? 为什么是右值引用?
// /*
//   右值引用是在C++11后所引入的新特性,这个特性的引入也使得左值与右值的本质发生改变
//   右值与左值一样也是一种表示数据的表达式;
//   一般的右值引用有字面常量,表达式返回值,函数返回值等等;
//   右值可以出现在赋值符号的右边但是不能出现在赋值符号的左边且右值不能通过取地址符号来取到其地址

// */

// int main() {
//   double x = 1.1, y = 2.2;

//   //常见的右值
//   999;
//   x + y;
//   fmin(x, y);//包含 <math.h>头文件或<cmath> 该函数返回的是一个临时变量 具有常性

//   //对右值进行右值引用
//   int &&rr1 = 999;
//   double &&rr2 = x + y;
//   double &&rr3 = fmin(x ,y);

//     /*
//         将会出现报错
//         右值不能放置于赋值符号的左侧
//         10 = 1;
//         x + y = 2.2;
//         fmin(x, y) = 21;
//     */

//   return 0;
// }

// ########################################

// 左值引用与右值引用
/*
    - 左值引用
    左值引用既可以引用左值也可以引用右值
    左值引用在引用左值时可以进行直接引用 
    但是左值引用若是需要引用右值或是引用带有常属性的左值时需要使用const引用 避免权限的放大

    - 右值引用
    右值引用可直接引用右值但其不能直接引用左值；
    若是直接引用左值将会发生error报错(左值包括具有常属性的左值)
    但若是需要使用右值引用引用左值时可以使用 move() 使得编译器能够在引用的过程当中将左值识别成右值
    move() 将在下面提到

*/

// int main() {
//     //左值引用
//     int a = 0;
//     int b = 23;
//     const int c = 30;
//     int &ar = a;
//     int &br = b; 
//     const int &cr = c; //const 引用引用具有常性的左值
//     const double &dr = 2.22;//const引用引用右值

//     //右值引用
//     int &&crr = 30;
//     char &&charrr = 'c';

//     cout << b << endl;
//     // int &&brr = b; 直接引用将会报错
//     int &&brr = move(b);
//     cout << b << endl;

//     return 0;

//  } 
// void Func(int &a) { cout << "左值引用 : void Func(int &a)" << endl; }
// void Func(int &&a) { cout << "右值引用 : void Func(int &&a)" << endl; }

// int main() {
//   int a = 10;
//   int b = 20;


//  由于函数的接收参数类型不同故构成重载
//   Func(a);  // 左值引用 : void Func(int &a)
//   Func(a + b);  //a+b为临时对象 右值引用 : void Func(int &&a)
//   return 0;
// }

//   //--------

// int main()
// {

//     //在右值中一般可以分为两种
//     /* 
//         1.字面值 - 即内置类型中的字面常量
//         2.将亡值 - 将亡值一般指自定义类型中的临时对象(即将销毁)
//     */

//    int a = 10;
//    int &&ar = move(a);
//    /*
//     move真正针对的是在堆种有具体资源的数据
//     而此处的a为内置类型 在堆中并不存在具体资源 故在此若是对a进行move操作+右值引用时并不会发生资源转移
//     且内置类型中的移动构造与拷贝构造的功能相同 只是单纯的复制粘贴
//    */

//    string s1("hello");
//    string s2 = "world";  //"world"为右值 且为字面值
//    string s3 = s1 + s2;  // s3为左值 s1+s2为右值且是一个临时对象 且为将亡值

//    // 该处将引用自定义写的string进行演示
//    Sweet::string st1("hello world");
//    Sweet::string st2("hello world");
//    Sweet::string ret1 = move(st1);
//    Sweet::string st3 = (st2 + 'c');
//    // 该种情况下编译器不同不一定一定会发生移动构造(编译器也许会优化)
//    // 故在使用右值引用的过程当中需要配合move或者完美转发进行使用

//    /*
//         对于左值引用而言 左值引用直接减少了拷贝:
//             1.左值引用传参 2.左值引用返回;
//         但对应的引用实际上不能再函数内的局部对象进行使用(局部对象将会在出作用域的时候被销毁)
//    */

//    return 0;
// }

//   //--------


// int& func1() {
//   int a = 10;
//   return a;
// }

/*
    一般情况下对于函数的返回值返回对于函数内的局部变量
    无论是自定义类型还是内置类型凡是局部都不能使用传左值引用返回或是传右值引用返回
    在使用返回之后无论是左值引用还是右值引用都是引用
    在引用过后
*/

// int main() {
//   int a = func1();
//   cout << a << endl;
//   return 0;
// }

// //   //--------

// // to_string函数
// // to_string函数
// namespace Sweet
// {
// Sweet::string to_string(int value) {
//   bool flag = true;
//   if (value < 0) {
//     flag = false;
//     value = 0 - value;
//   }

//   Sweet::string str;

//   while (value) {
//     int x = value % 10;
//     value /= 10;
//     str += ('0' + x);
//   }

//   if (!flag) {
//     str += '-';
//   }

//   std::reverse(str.begin(), str.end());
//   return str;
// }    
// } // namespace Sweet


//   int main() {
//     Sweet::string s1 = Sweet::to_string(1234);
//     /*
//         该种情况下若是不存在移动构造其将会去调用对应的拷贝构造
//         存在移动构造时可能会试图将 `return str;`
//        时的返回值识别成右值并进行移动构造 当然在不同编译器中其标准将会不同
//        例如vs中的msvc编译器和在Linux上所使用的g++编译器行为有所不同


//         在msvc中 若是不存在移动构造则将会调用两次拷贝构造 会被优化成直接构造

//                 若是存在移动构造则会调用一次移动构造和拷贝构造
//                 会被优化成移动构造

//         在g++中 若是不存在移动构造则会调用两次拷贝构造
//                 但会被g++编译器根据返回值优化（RVO）或命名返回值优化（NRVO）的规则对返回的对象进行优化
//                 避免额外的拷贝或移动构造;
//                 对于存在移动构造时对于g++来说也是如此;
//                 g++ (GCC) 8.3.1 20190311 (Red Hat 8.3.1-3) 版本
//     */

//     // 假设存在代码为 int a; move(a); a再被move后仍然是左值 但是move(a) 的这个表达式的返回值为右值
//     return 0;
//     //再C++11过后 所有的STL容器都增加了一个移动构造(通过右值引用)
//   }

// //   //--------

// /*
//     在C++11过后不仅在所有STL中添加了移动构造 还在所有的插入数据接口函数当中添加了对应的右值引用版本
//     对于右值引用版本的插入数据接口函数而言主要针对的是对于模板中以自定义类型为模板参数的容器的插入 
//     若是容器对应的模板参数为自定义类型时 在插入过程中可能会经历对应的拷贝构造 对应的性能将会降低
//     而再添加了右值引用版本的插入接口函数时可以直接进行移动构造来移动自定义类型的资源从而真正意义上减少拷贝
// */

// int main() {
//   list<Sweet::string> ls1;
//   Sweet::string s1("hello world");
//   ls1.push_back(s1);
//   ls1.push_back(move(s1));
//   //在此处还会出现一个风险 由于s1中的资源被转移 故在s1被move之后其状态为悬空状态不具备任何资源

//   cout << "--------------------" << endl;
//   ls1.push_back(Sweet::string("hello"));//匿名对象为右值
//   ls1.push_back("world");//单参构造支持隐式类型转换 构造出来的为临时对象为右值
//   return 0;

//   /*
//     结果为
//         Sweet::string::string(const char* str) 直接构造
//         Sweet::string::string(const string& str) 拷贝构造
//         Sweet::string::string(const char* str) 直接构造
//         Sweet::string::string(string&& str) 移动构造
//         //此处的直接构造可以不用观察 
//         //其中第一个直接构造为构造s1时进行的直接构造
//         //第二个直接构造为拷贝构造中的构造string tmp进行的直接构造
 
    
//     在实际当中 左值引用与右值引用都可以减少拷贝提高效率
//     但对于左值引用而言,其直接减少了拷贝构造的发生
//     右值引用则为间接减少拷贝,在右值引用当中其将会识别是左值还是右值,若是右值则直接进行移动构造(移动资源)不再进行深拷贝从而提高效率
//     (左值也可进行move并移动构造 但是左值[非内置类型]再进行移动构造后其将会变为悬空变量)
//   */


//     }

// ########################################

template<class T>
void fun(T& t){
  cout << "void fun(T& t) 左值引用" << endl;
}

template <class T>
void fun(T&& t) {
  cout << "void fun(T&& t) 右值引用" << endl;
}

//万能引用 T&&
template<class T>
void Func(T&& t){
  fun<T>(forward<T>(t));//此处的forward为完美转发
}
/*
    引用折叠依赖于万能引用与完美转发
    当使用万能引用时并未进行完美转发时可能将会造成数据的属性丢失
即右值的属性被接收时将会在函数内判定为左值
    完美转发能够将万能引用所绑定的左、右值进行对应的左值、右值引用
    且引用折叠的规则为:
     - 当万能引用绑定到一个左值时万能引用将会折叠为一个左值引用
     - 当万能引用绑定到一个右值时万能引用将会折叠为一个右值引用


    右值引用引用过后识别成左值的主要例子可以为
    Sweet::string::string(string&& str) : _str(nullptr) {
        cout << "Sweet::string::string(string&& str) 移动构造" << endl;
        swap(str);
    在该段代码当中由于需要在右值引用过后需要对其进行swap操作 而若是还是右值的话则不能进行swap操作 
}

*/

int main() {
  int a = 10;

  Func<decltype(10)>(10);  
//   Func(10);  // 自动推导
  Func(a);
  return 0;
}